package com.wsdk.service;

import android.app.Notification;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;

import com.wsdk.R;
import com.wsdk.Wsdk;
import com.wsdk.constants.GlobalData;
import com.wsdk.util.ControlUtil;
import com.wsdk.util.LogUtil;
import com.wsdk.util.ShowToast;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BluetoothLeService
        extends Service {
    private final static String  ACTION_LABEL = "com.seal.le";
    public final static String ACTION_GATT_CONNECTED = ACTION_LABEL + ".ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED = ACTION_LABEL +
            ".ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED = ACTION_LABEL +
            ".ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_GATT_HARDWARE_CONNECTED = ACTION_LABEL +
            ".ACTION_GATT_HARDWARE_CONNECTED";
    public final static String ACTION_DATA_AVAILABLE = ACTION_LABEL + ".ACTION_DATA_AVAILABLE";

    public final static String ACTION_DEVICE_LOW_POWER = ACTION_LABEL + ".ACTION_DEVICE_LOW_POWER";
    public final static String ACTION_DEVICE_UPLEVEL_FAILED = ACTION_LABEL +
            ".ACTION_DEVICE_UPLEVEL_FAILED";

    public final static String EXTRA_DATA = ACTION_LABEL + ".EXTRA_DATA";

    public final static UUID UUID_NOTIFY = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb");
    public final static UUID UUID_SERVICE = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");

    public static BluetoothGattCharacteristic mNotifyCharacteristic;
    private static BluetoothAdapter mBluetoothAdapter;
    private static BluetoothGatt mBluetoothGatt;
    private final IBinder mBinder = new LocalBinder();
    private BluetoothManager mBluetoothManager;
    public static boolean isConnecting = false;

    private List<Byte> list = new ArrayList<>();
    private List<Byte> storeList = new ArrayList<>();

    private Handler mainHandler = new Handler(Looper.getMainLooper());

    private static WeakReference<Context> weakReferenceContext;

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            LogUtil.d("Bluetooth State:status=" + status + " NewStatus=" + newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mBluetoothGatt = gatt;
                broadcastUpdate(ACTION_GATT_CONNECTED, gatt.getDevice());
                LogUtil.d("Bluetooth State:Connected to GATT server.");
                LogUtil.d("Bluetooth State:Attempting to start service discovery:" + mBluetoothGatt
                        .discoverServices());
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                if (mBluetoothGatt != null) {
                    mBluetoothGatt.close();
                }
                mBluetoothGatt = null;
                broadcastUpdate(ACTION_GATT_DISCONNECTED, gatt.getDevice());
                LogUtil.d("Bluetooth State:Disconnected from GATT server.");
            }
            // here to ensure not connecting
            isConnecting = false;
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                LogUtil.d("Bluetooth State:onServicesDiscovered received");
                findService(gatt.getServices());
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                LogUtil.d("Bluetooth State:OnCharacteristicRead");
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            LogUtil.d("Bluetooth State:OnCharacteristicChanged");
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic, int status) {
            LogUtil.d("Bluetooth State:OnCharacteristicWrite");
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor bd, int status) {
            LogUtil.d("Bluetooth State:onDescriptorRead");
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor bd, int status) {
            LogUtil.d("Bluetooth State:onDescriptorWrite");
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int a, int b) {
            LogUtil.d("Bluetooth State:onReadRemoteRssi");
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int a) {
            LogUtil.d("Bluetooth State:onReliableWriteCompleted");
        }

    };

    public static void writeValue(final byte[] cmd) {
        if (mBluetoothGatt == null) {
            ShowToast.shortTime("蓝牙异常断开，请尝试重新连接");
            GlobalData.isConnect = false;
            /*
              jump to update
             */
//            if(cmd[3] == 0x1f && cmd[4] == 0x01){
//                LogUtil.d("转入升级失败");
            if (weakReferenceContext != null) {
                Context context = weakReferenceContext.get();
                if (context != null) {
                    LogUtil.d("Bluetooth Update Failed, I catch it");
                    context.sendBroadcast(new Intent(ACTION_DEVICE_UPLEVEL_FAILED));
                }
            }
//            }
            return;
        }
        mNotifyCharacteristic.setValue(cmd);
        mBluetoothGatt.writeCharacteristic(mNotifyCharacteristic);
    }

    private void findService(List<BluetoothGattService> gattServices) {
        LogUtil.d("Bluetooth State:Service Count is:" + gattServices.size());
        for (BluetoothGattService gattService : gattServices) {
            LogUtil.d("Bluetooth State:suuid:" + gattService.getUuid().toString());
            LogUtil.d("Bluetooth State:suuid_service:" + UUID_SERVICE.toString());
            if (gattService.getUuid().toString().equalsIgnoreCase(UUID_SERVICE.toString())) {
                List<BluetoothGattCharacteristic> gattCharacteristics = gattService
                        .getCharacteristics();
                LogUtil.d("Bluetooth State:Characteristic Count is:" + gattCharacteristics.size());
                for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                    LogUtil.d("Bluetooth State:cuuid:" + gattCharacteristic.getUuid().toString());
                    LogUtil.d("Bluetooth State:cuuid_notify:" + UUID_NOTIFY.toString());
                    if (gattCharacteristic.getUuid().toString().equalsIgnoreCase(
                            UUID_NOTIFY.toString())) {
                        mNotifyCharacteristic = gattCharacteristic;
                        setCharacteristicNotification(gattCharacteristic);

                        // cause of HARDWARE CONNECT maybe too fast
                        mainHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                broadcastUpdate(ACTION_GATT_HARDWARE_CONNECTED);
                            }
                        }, 500);

                        return;
                    }
                }
            }
        }
    }

    private void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    private void broadcastUpdate(String action, BluetoothDevice bluetoothDevice) {
        Intent intent = new Intent(action);
        intent.putExtra("BLUETOOTH_DEVICE", bluetoothDevice);
        sendBroadcast(intent);
    }

    private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);

        byte[] data = characteristic.getValue();

        if (data != null && data.length > 0) {
            for (byte aData : data) {
                storeList.add(aData);
            }
            LogUtil.e("Bluetooth Receive Data:" + ControlUtil.bytesToHexString(data));
            if (storeList.get(0) == 0x02 && storeList.get(storeList.size() - 1) == 0x03) {
                if (isLast03(storeList)) {
                    int i = 0;
                    while (i < storeList.size()) {
                        if (storeList.get(i) == 0x10) {
                            list.add(storeList.get(i+1));
                            i += 2;
                        } else {
                            list.add(storeList.get(i));
                            i++;
                        }
                    }
                    byte[] bData = new byte[list.size()];
                    for (int j = 0; j < list.size(); j++) {
                        bData[j] = list.get(j);
                    }
                    LogUtil.e("Bluetooth Receive Final Data:" + ControlUtil.bytesToHexString(bData));
                    list.clear();
                    storeList.clear();
                    /*
                      Power lower 15 percent
                     */
                    if (bData[4] == (byte) 0xF9) {
                        broadcastUpdate(ACTION_DEVICE_LOW_POWER);
                        broadcastUpdate(ACTION_DEVICE_UPLEVEL_FAILED);
                        Looper.prepare();
                        Toast.makeText(Wsdk.getInstance(), "信印电量过低,请充电", Toast.LENGTH_SHORT)
                                .show();
                        Looper.loop();
                        return;
                    } else if(bData[4] == (byte) 0xED){
//                    toReportBug();
                        Looper.prepare();
                        Toast.makeText(Wsdk.getInstance(), "自检出错",
                                Toast.LENGTH_SHORT).show();
                        Looper.loop();
                        return;
                    }
                    intent.putExtra(EXTRA_DATA, bData);

                    sendBroadcast(intent);
                }
            }
        }
    }

    /**
     * 判断字节03是否为包结尾
     * @param list list
     * @return boolean
     */
    private boolean isLast03(List<Byte> list) {
        int i = list.size() - 2;
        int count = 0;
        while (list.get(i) == 0x10) {
            count++;
            i--;
        }
        // 成对10存在，表示校验和或者包括校验和在内之前数据为10，03位结尾
        // 奇数10存在，表示03为数据非结尾
        return count % 2 == 0;
    }

    private boolean initialize() {
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                LogUtil.d("Bluetooth State:Unable to initialize BluetoothManager.");
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            LogUtil.d("Bluetooth State:Unable to obtain a BluetoothAdapter.");
            return false;
        }
        return true;
    }

    private boolean connect(String address) {
        if (isConnecting) {
            return false;
        }
        isConnecting = true;
        if (mBluetoothAdapter == null || address == null) {
            LogUtil.d("Bluetooth State:BluetoothAdapter not initialized or unspecified address.");
            isConnecting = false;
            return false;
        }
        /*
          get device according to mac address
         */
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            LogUtil.d("Bluetooth State:Device not found. Unable to connect.");
            isConnecting = false;
            return false;
        }

        if (!isConnecting && mBluetoothGatt != null) {
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        // for connectGatt longtime no feedback
        mainHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                isConnecting = false;
            }
        }, 10000);

        LogUtil.d("Bluetooth State:Trying to create a new connection.");
        return true;
    }

    public static void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.d("Bluetooth State:BluetoothAdapter not initialized");
            GlobalData.isConnect = false;
            return;
        }
        mBluetoothGatt.disconnect();
        GlobalData.isConnect = false;
    }

    /**
     * 使用给定的BLE设备后,应用程序必须调用这个方法,以确保正确地释放资源。
     */
    private void close() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        GlobalData.isConnect = false;
    }

    private void setCharacteristicNotification(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.d("Bluetooth State:BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, true);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (initialize()) {
            LogUtil.e("BluetoothLeService Started");
            if (intent == null || intent.getStringExtra("DEVICE_ADDRESS") == null) {
                LogUtil.e("Bluetooth Address:null");
                return super.onStartCommand(intent, flags, startId);
            }
            String deviceAddress = intent.getStringExtra("DEVICE_ADDRESS");
            LogUtil.e("Bluetooth Address:" + deviceAddress + " [Result]:" + connect(deviceAddress));
            /*
              开启前台服务，保证蓝牙服务一直处于运行状态
             */
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this,
                    "DEFAULT_CHANNEL");
            Notification notification = builder.setSmallIcon(R.mipmap.ic_logo).setTicker(
                    "蓝牙通信服务已启动").setContentTitle("\"信印\"蓝牙通信服务").setContentText("\"信印\"蓝牙通信服务")
                    .build();
            startForeground(1, notification);
        } else {
            Looper.prepare();
            ShowToast.shortTime("该手机不支持蓝牙4.0");
            Looper.loop();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        close();
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        weakReferenceContext = new WeakReference<Context>(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        close();
        mainHandler.removeCallbacksAndMessages(null);
    }

    private class LocalBinder
            extends Binder {
        public BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }
}
